package com.eight.paper.admin.service.impl;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.data.DocumentRenderData;
import com.deepoove.poi.data.Documents;
import com.eight.common.config.EightConfig;
import com.eight.common.constant.ExamConstants;
import com.eight.common.core.domain.model.LoginUser;
import com.eight.common.core.redis.RedisCache;
import com.eight.common.exception.ServiceException;
import com.eight.common.utils.*;
import com.eight.common.utils.bean.BeanUtils;
import com.eight.common.utils.file.FileUploadUtils;
import com.eight.paper.admin.domain.*;
import com.eight.paper.admin.domain.model.*;
import com.eight.paper.admin.domain.model.enums.*;
import com.eight.paper.admin.mapper.*;
import com.eight.paper.admin.service.IExamPaperExamineeService;
import com.eight.paper.admin.service.IExamPaperImageService;
import com.eight.paper.admin.service.IExamPaperService;
import com.eight.paper.admin.service.IQuestionBaseService;
import com.eight.paper.admin.util.FileUtil;
import com.eight.paper.admin.util.NumUtil;
import com.eight.paper.admin.util.PoitlUtil;
import com.eight.paper.admin.util.QuestionUtil;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static com.eight.common.utils.SecurityUtils.getLoginUser;
import static com.eight.common.utils.SecurityUtils.getUserId;
import static com.eight.paper.admin.domain.model.enums.QuestionTypeEnum.*;

/**
 * 考试试卷Service业务层处理
 * 
 * @author eight
 * @date 2023-10-31
 */
@Service
public class ExamPaperServiceImpl implements IExamPaperService 
{
    private static final Logger log = LoggerFactory.getLogger(ExamPaperServiceImpl.class);
    @Autowired
    private ExamPaperMapper examPaperMapper;
    @Autowired
    private ExamPaperGroupMapper examPaperGroupMapper;
    @Autowired
    private ExamPaperQuestionMapper examPaperQuestionMapper;
    @Autowired
    private ExamPaperConfigMapper examPaperConfigMapper;
    @Autowired
    private QuestionVersionMapper questionVersionMapper;
    @Autowired
    private ExamPaperExamineeMapper examPaperExamineeMapper;
    @Autowired
    private ExamPaperAnswerMapper examPaperAnswerMapper;
    @Autowired
    private QuestionBaseMapper questionBaseMapper;
    @Autowired
    private IQuestionBaseService questionBaseService;
    @Autowired
    private IExamPaperExamineeService examPaperExamineeService;
    @Autowired
    private IExamPaperImageService examPaperImageService;
    @Autowired
    private RedisCache redisCache;
    @Resource
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Value("${face.arcsoft.libPath}")
    private String libPath;
    @Value("${face.arcsoft.faceSimilar}")
    private float faceSimilar;


    /**
     * 查询考试试卷
     * 
     * @param id 考试试卷主键
     * @return 考试试卷
     */
    @Override
    public ExamPaper selectExamPaperById(Integer id)
    {
        return selectExamPaperById(id, AnswerShowTypeEnum.True.getCode());
    }

    /**
     * 根据type获取是否带答案的试卷
     * @param id
     * @param type
     * @return
     */
    @Override
    public ExamPaperReponseBody selectExamPaperById(Integer id,String type)
    {
        ExamPaper paper = examPaperMapper.selectExamPaperById(id);
        if(ComposeTypeEnum.Dynamics.getCode().equals(paper.getComposeType())){
             return selectDynamicsExamPaper(paper,type);
        }else {
            return selectConfigExamPaper(paper,type);
        }
    }

    /**
     * 获取动态组卷
     * @param paper
     * @param type
     * @return
     */
    public ExamPaperReponseBody selectDynamicsExamPaper(ExamPaper paper,String type)
    {
        ExamPaperConfig config = new ExamPaperConfig();
        config.setPaperId(paper.getId());
        ExamPaperBody examPaperBody = new ExamPaperBody();
        BeanUtils.copyProperties(paper,examPaperBody);
        List<ExamPaperConfig> configs = examPaperConfigMapper.selectExamPaperConfigList(config);
        configs.stream().forEach(cg->{
            if(StringUtils.isNotBlank(cg.getLevels())){
                cg.setLevelList(cg.getLevels().split(", "));
            }
            if(StringUtils.isNotBlank(cg.getSections())){
                cg.setSectionList(cg.getSections().split(", "));
            }
        });
        examPaperBody.setConfigs(configs);
        ExamPaperReponseBody body = getQuestions(examPaperBody,type);
        if(StringUtils.isNotBlank(paper.getGrades())) {
            Integer[] ids = Arrays.stream(paper.getGrades().split(",")).map(Integer::valueOf).toArray(Integer[]::new);
            List<UserInfo> users = examPaperMapper.selectExamUserList(ids);
            body.setGradeUsers(users);
        }
        return body;
    }

    /**
     * 获取抽题组卷、手动组卷
     * @param paper
     * @param type
     * @return
     */
    public ExamPaperReponseBody selectConfigExamPaper(ExamPaper paper,String type)
    {
        ExamPaperReponseBody body = new ExamPaperReponseBody();
        ExamPaperConfig config = new ExamPaperConfig();
        config.setPaperId(paper.getId());
        List<ExamPaperConfig> configs = examPaperConfigMapper.selectExamPaperConfigList(config);
        body.setConfigs(configs);

        ExamPaperGroup group = new ExamPaperGroup();
        group.setPaperId(paper.getId());
        List<ExamPaperGroupBoby> groupBodyList = examPaperGroupMapper.selectExamPaperGroupBodyList(group);
        List<ExamPaperGroupReponse> groups = new ArrayList<>();
        groupBodyList.stream().forEach(g->{
            ExamPaperGroupReponse groupReponseBoby=new ExamPaperGroupReponse();
            BeanUtils.copyProperties(g, groupReponseBoby);
            List<QuestionBase> questions=new ArrayList<>();
            g.getQuestions().stream().forEach(q->{
                QuestionVersion version=questionVersionMapper.selectQuestionVersionById(q.getVersionId());
                if(version!=null){
                    QuestionBase questionBase = questionBaseService.selectQuestionBody(version.getBaseId(),q.getScore(),type,version.getId(),q.getPaperQuestionId(),q.getSubScores());
                    questions.add(questionBase);
                }
            });
            if(paper.getPatterns().contains("4")){
                Collections.shuffle(questions);
            }
            groupReponseBoby.setQuestions(questions);
            groups.add(groupReponseBoby);
        });
        if(StringUtils.isNotBlank(paper.getGrades())) {
            Integer[] ids = Arrays.stream(paper.getGrades().split(",")).map(Integer::valueOf).toArray(Integer[]::new);
            List<UserInfo> users = examPaperMapper.selectExamUserList(ids);
            body.setGradeUsers(users);
        }
        body.setGroups(groups);
        BeanUtils.copyProperties(paper, body);
        return body;
    }

    /**
     * 固定试卷、任务试卷，提交后自动完成阅卷
     * 时段试卷，提交后客观题自动完成阅卷，状态为待阅卷
     * @param paperId 试卷id
     * @param paper 答题信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitPaper(Integer paperId, JSONObject paper) {
        Date nowDate = DateUtils.getNowDate();
        Long userId = SecurityUtils.getUserId();
        ExamPaperExaminee examPaperExaminee = initExamPaperExaminee(paperId,userId.intValue(),nowDate,"");

        ExamPaperGroup group = new ExamPaperGroup();
        group.setPaperId(paperId);
        List<ExamPaperGroupBoby> groupBodyList = examPaperGroupMapper.selectExamPaperGroupBodyList(group);
        HashMap questionMap = new HashMap();
        groupBodyList.stream().forEach(g->{
            g.getQuestions().stream().forEach(q->{
                questionMap.put(q.getPaperQuestionId(),q);
            });
        });

        ExamPaperConfig query = new ExamPaperConfig();
        query.setPaperId(paperId);
        List<ExamPaperConfig> configs = examPaperConfigMapper.selectExamPaperConfigList(query);
        ComposeTypeEnum composeTypeEnum = ComposeTypeEnum.fromCode(paper.getString("composeType"));

        JSONArray groups = paper.getJSONArray(ExamConstants.PAPER_GROUPS);
        List<ExamPaperAnswerBody> paperAnswers = new ArrayList<>();
        Integer examineeId = examPaperExaminee.getId();
        BigDecimal totalScore = BigDecimal.ZERO;
        if(StringUtils.isNotNull(groups)&&groups.size()>0){
           for(int i=0;i<groups.size();i++){
               BigDecimal defaultScore = BigDecimal.ZERO;
               if(composeTypeEnum.equals(ComposeTypeEnum.Dynamics)){
                   for(ExamPaperConfig config:configs){
                       if(groups.getJSONObject(i).getInteger("configId").equals(config.getId())){
                           defaultScore = config.getDefaultScore();
                       }
                   }
               }
               JSONObject groupJson = groups.getJSONObject(i);
               JSONArray questions =groupJson.getJSONArray(ExamConstants.PAPER_QUESTIONS);
               if(StringUtils.isNotNull(questions)&&questions.size()>0){
                   for(int j=0;j<questions.size();j++) {
                       JSONObject question = questions.getJSONObject(j);
                       String questionType = question.getString(ExamConstants.QUESTIONTYPE);
                       BigDecimal questionTotelScore = buildExamineeAnswers(examineeId,questionType,question,paperAnswers,nowDate,questionMap,composeTypeEnum,defaultScore);
                       totalScore = totalScore.add(questionTotelScore);
                   }
               }
           }
            paper.put("totalScore",totalScore);
            String key = String.format("%s:%s:%s", ExamConstants.TEMP_MARK,paperId,userId);
            redisCache.setCacheObject(key,paper);
           //异步存储答题信息到数据库
            threadPoolTaskExecutor.execute(() -> {
                examPaperAnswerMapper.insertExamPaperAnswers(paperAnswers);
                submitMark(paperId,examineeId,paper,null);
                //不再区分试卷类型，提交后默认阅卷
                /*ExamPaper examPaper = examPaperMapper.selectExamPaperById(paperId);
                if(PaperTypeEnum.Fixed.getCode().equals(examPaper.getPaperType())||PaperTypeEnum.Task.getCode().equals(examPaper.getPaperType())){
                    submitMark(paperId,examineeId,paper,null);
                }*/
            });
        }
    }

    /**
     * 暂存试卷
     * @param paperId
     * @param paper
     */
    @Override
    public void tempPaper(Integer paperId, JSONObject paper) {
        Long userId = SecurityUtils.getUserId();
        String key = String.format("%s:%s:%s", ExamConstants.TEMP_EXAM,paperId,userId);
        redisCache.setCacheObject(key,paper);
    }

    /**
     * 暂存考生批卷信息
     * @param paperId
     * @param userId
     * @param paper
     */
    @Override
    public void tempMark(Integer paperId, Integer userId, JSONObject paper) {
        String key = String.format("%s:%s:%s", ExamConstants.TEMP_MARK,paperId,userId);
        redisCache.setCacheObject(key,paper);
    }

    /**
     * 阅卷提交处理
     * @param paperId
     * @param examineeId
     * @param paper
     */
    @Override
    public void submitMark(Integer paperId, Integer examineeId, JSONObject paper, Integer userId) {
       /* ExamPaperGroup group = new ExamPaperGroup();
        group.setPaperId(paperId);
        List<ExamPaperGroupBoby> groupBodyList = examPaperGroupMapper.selectExamPaperGroupBodyList(group);
        HashMap questionMap = new HashMap();
        groupBodyList.stream().forEach(g->{
            g.getQuestions().stream().forEach(q->{
                questionMap.put(q.getPaperQuestionId(),q);
            });
        });
*/
        JSONArray groups = paper.getJSONArray(ExamConstants.PAPER_GROUPS);
        if(StringUtils.isNotNull(groups)&&groups.size()>0){
            for(int i=0;i<groups.size();i++){
                JSONObject groupJson = groups.getJSONObject(i);
                JSONArray questions =groupJson.getJSONArray(ExamConstants.PAPER_QUESTIONS);
                if(StringUtils.isNotNull(questions)&&questions.size()>0){
                    for(int j=0;j<questions.size();j++) {
                        JSONObject question = questions.getJSONObject(j);
                        question.put(ExamConstants.MARK,1);
                        String questionType = question.getString(ExamConstants.QUESTIONTYPE);
                        if(GapFilling.getCode().equals(questionType)||Explain.getCode().equals(questionType)||ShortAnswer.getCode().equals(questionType)||Analysis.getCode().equals(questionType)||Discuss.getCode().equals(questionType)) {
                            QuestionAnswerBody submitBody = JSONObject.parseObject(question.toJSONString(), QuestionAnswerBody.class);
                            submitBody.getAnswers().stream().forEach(answer -> {
                                ExamPaperAnswerBody examPaperAnswer = new ExamPaperAnswerBody();
                                examPaperAnswer.setExamineeId(examineeId);
                                examPaperAnswer.setSeqNo(Integer.parseInt(answer.getSeqCode()));
                                examPaperAnswer.setPaperQuestionId(submitBody.getPaperQuestionId());
                                examPaperAnswer.setScore(answer.getExamineeScore());
                                BigDecimal examineeScore = answer.getExamineeScore();
                                int mark =1;
                                if(examineeScore==null){
                                    examineeScore = BigDecimal.ZERO;
                                    mark = 0;
                                }
                                examPaperAnswer.setCorrect(examineeScore.compareTo(BigDecimal.ZERO)>0?1:0);
                                examPaperAnswer.setMark(mark);
                                examPaperAnswerMapper.updateExamPaperAnswerByMark(examPaperAnswer);
                            });
                        }
                    }
                }
            }
        }
        String comment = paper.getString(ExamConstants.COMMENT);
        BigDecimal totalScore = examPaperAnswerMapper.sumExamineeScore(examineeId);
        ExamPaperExaminee examPaperExaminee = new ExamPaperExaminee();
        examPaperExaminee.setId(examineeId);
        examPaperExaminee.setTotalScore(totalScore);
        examPaperExaminee.setGradeId(userId);
        examPaperExaminee.setGradeTime(DateUtils.getNowDate());
        examPaperExaminee.setStatus("2");
        examPaperExaminee.setComment(comment);
        examPaperExamineeMapper.updateExamPaperExaminee(examPaperExaminee);
    }

    /**
     * 查询单个考生试卷列表
     *
     * @param examPaper 考试试卷
     * @return 考试试卷集合
     */
    @Override
    public List<ExamPaperBody> selectExamineePaperList(ExamPaper examPaper) {
        return examPaperMapper.selectExamineePaperList(examPaper);
    }

    /**
     * 查询单个阅卷老师试卷列表
     *
     * @param examPaper 考试试卷
     * @return 考试试卷集合
     */
    @Override
    public List<ExamPaper> selectMarkPaperList(ExamPaper examPaper) {
        return examPaperMapper.selectMarkPaperList(examPaper);
    }

    /**
     * 动态获取试题
     * @param examPaperBody
     * @param type 是否显示答案，1是0否
     * @return
     */
    @Override
    public ExamPaperReponseBody getQuestions(ExamPaperBody examPaperBody,String type) {
        String[] levelList = examPaperBody.getLevelList();
        String[] sectionList = examPaperBody.getSectionList();
        ExamPaperReponseBody body = new ExamPaperReponseBody();
        List<ExamPaperGroupReponse> groups = new ArrayList<>();
        examPaperBody.getConfigs().stream().forEach(config->{
            String questionType = config.getQuestionType();
            if(StringUtils.isNotNull(config.getQuestionAmount())&&config.getQuestionAmount()>0){
                ExamPaperGroupReponse groupReponseBoby=new ExamPaperGroupReponse();
                //存储config表id
                groupReponseBoby.setConfigId(config.getId());
                groupReponseBoby.setGroupName(QuestionTypeEnum.fromCode(questionType).getName());
                groupReponseBoby.setDefaultScore(config.getDefaultScore());
                String[] configLevelList = config.getLevelList();
                String[] configSectionList = config.getSectionList();
                if(StringUtils.isNotEmpty(levelList)){
                    configLevelList =levelList;
                }
                if(StringUtils.isNotEmpty(sectionList)){
                    configSectionList =sectionList;
                }
                if(StringUtils.isEmpty(configSectionList)){
                    throw new ServiceException(String.format("‘%s’未选择题库",groupReponseBoby.getGroupName()));
                }
                List<QuestionBase> questions = new ArrayList<>();
                List<QuestionBase> list = questionBaseMapper.selectQuestionBaseListRandom(questionType,config.getQuestionAmount(),configSectionList,configLevelList);
                if(list.size()<config.getQuestionAmount()){
                    throw new ServiceException(String.format("%s:所选条件下，最多只能获取%s条试题,组卷失败！",groupReponseBoby.getGroupName(),list.size()));
                }
                for(QuestionBase questionBase:list){
                    QuestionBase question = questionBaseService.selectQuestionBody(questionBase,config.getDefaultScore(),type,null,null,"");
                    questions.add(question);
                }
                groupReponseBoby.setQuestions(questions);
                groups.add(groupReponseBoby);
            }
        });
        BeanUtils.copyProperties(examPaperBody, body);
        body.setGroups(groups);
        return body;
    }

    /**
     * 初始化考生答卷总表信息，如果已存在答卷明细则清空
     * @param paperId
     * @param userId
     * @param nowDate
     * @return
     */
    @Override
    public ExamPaperExaminee initExamPaperExaminee(Integer paperId,Integer userId,Date nowDate,String questionOrder){
        ExamPaperExaminee query = new ExamPaperExaminee();
        query.setPaperId(paperId);
        query.setUserId(userId);
        List<ExamPaperExamineeResponse> examinees = examPaperExamineeMapper.selectExamPaperExamineeList(query);
        if(examinees.size()>0){
            examPaperAnswerMapper.deleteExamPaperAnswerByExamineeId(examinees.get(0).getId());
            return examinees.get(0);
        }else {
            ExamPaperExaminee examPaperExaminee = new ExamPaperExaminee();
            examPaperExaminee.setPaperId(paperId);
            examPaperExaminee.setUserId(userId);
            examPaperExaminee.setStatus("1");
            examPaperExaminee.setSubmitTime(nowDate);
            examPaperExaminee.setQuestionOrder(questionOrder);
            examPaperExaminee.setTotalScore(BigDecimal.ZERO);
            examPaperExaminee.setCreateTime(nowDate);
            examPaperExamineeMapper.insertExamPaperExaminee(examPaperExaminee);
            return examPaperExaminee;
        }
    }

    /**
     * 导出对应试卷所有考生考试列表
     * @param id
     * @param uuid
     */
    @Override
    public void exportPapersById(Integer id, String uuid) {
        ExamPaperReponseBody examPaper = selectExamPaperById(id, AnswerShowTypeEnum.True.getCode());
        //查询考生成绩
        ExamPaperExaminee examPaperExaminee = new ExamPaperExaminee();
        examPaperExaminee.setPaperId(id);
        List<ExamPaperExamineeResponse> list = examPaperExamineeService.selectExamPaperExamineeList(examPaperExaminee);
        redisCache.setCacheObject(String.format(ExamConstants.EXAM_EXPORT_PRESET_AMOUNT,uuid), list.size()+2);
        AtomicInteger successAmount = new AtomicInteger();
        redisCache.setCacheObject(String.format(ExamConstants.EXAM_EXPORT_SUCCESS_AMOUNT,uuid), successAmount);

        List<Integer> questionTypes = getQuestionTypes(id);
        Map<String, Object> allData = new HashMap<>();
        String examineeInfo = String.format("科室：%s姓名：%s考号：%s成绩：%s", formatExamineeInfo(""), formatExamineeInfo(""), formatExamineeInfo(""), formatExamineeInfo(""));
        allData.put("examineeInfo", examineeInfo);
        // 本地资源路径
        String localPath = EightConfig.getProfile();
        String dirPath = localPath+ File.separator +uuid;
        Path directory = Paths.get(dirPath);
        if (!Files.exists(directory)) {
            try {
                Files.createDirectories(directory);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        List<File> files = new ArrayList<>();
        String filePath = "";
        filePath = createWord(examPaper,questionTypes,examPaper.getPaperName(),false,false,"空白试卷"+System.currentTimeMillis(),allData,dirPath);
        files.add(new File(filePath));
        redisCache.setCacheObject(String.format(ExamConstants.EXAM_EXPORT_SUCCESS_AMOUNT,uuid), successAmount.incrementAndGet());
        filePath = createWord(examPaper,questionTypes,examPaper.getPaperName(),false,true,"答案试卷"+System.currentTimeMillis(),allData,dirPath);
        files.add(new File(filePath));
        redisCache.setCacheObject(String.format(ExamConstants.EXAM_EXPORT_SUCCESS_AMOUNT,uuid), successAmount.incrementAndGet());

        //导出所有考生试卷
        List<ExamineeAnswer> examineeAnswers = examPaperAnswerMapper.selectExamineeAnswers(examPaper.getId());
        for (ExamPaperExamineeResponse examinee:list){
            allData = new HashMap<>();
            String userName = formatExamineeInfo(examinee.getUserName());
            String nickName = formatExamineeInfo(examinee.getNickName());
            String dptName = formatExamineeInfo(examinee.getDeptName());
            String totalScore = formatExamineeInfo(examinee.getTotalScore().toString());
            examineeInfo = String.format("科室：%s姓名：%s考号：%s成绩：%s", dptName, nickName, userName,totalScore);
            allData.put("examineeInfo", examineeInfo);
            if(ComposeTypeEnum.Dynamics.getCode().equals(examPaper.getComposeType())){

            }else {
                List<ExamineeAnswer> currentExamineeAnswers = examineeAnswers.stream().filter(a->examinee.getId().equals(a.getExamineeId())).collect(Collectors.toList());
                ExamPaperReponseBody examineePaper = getExamineePaperFromDB(examPaper,currentExamineeAnswers);
                try{
                    filePath = createWord(examineePaper,questionTypes,examPaper.getPaperName(),true,true,examinee.getUserName()+System.currentTimeMillis(),allData,dirPath);
                    files.add(new File(filePath));
                    redisCache.setCacheObject(String.format(ExamConstants.EXAM_EXPORT_SUCCESS_AMOUNT,uuid), successAmount.incrementAndGet());
                }catch (Exception e){
                    String failInfo = String.format("%s导出异常:%s",examinee.getUserName(),e.getMessage());
                    redisCache.setCacheLeftPush(String.format(ExamConstants.EXAM_EXPORT_FAIL_INFO,uuid), failInfo);
                }
            }
        }
        String zipFileName = "exam_" + uuid + ".zip";
        String zipPath = localPath + File.separator+"zip";
        Path zipDirectory = Paths.get(zipPath);
        if (!Files.exists(zipDirectory)) {
            try {
                Files.createDirectories(zipDirectory);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        String zipFilePath = zipPath + File.separator + zipFileName;
        try (OutputStream out = Files.newOutputStream(Paths.get(zipFilePath))) {
            ZipUtils.toZip(files, out, zipPath);
            //FileUtil.deleteAllFile(dirPath);
        } catch (Exception e) {
            log.error("导出试卷失败：", e);
            throw new ServiceException("导出试卷失败");
        }
    }

    /**
     * 格式化考生信息，用于试卷导出
     * @param text
     * @return
     */
    private String formatExamineeInfo(String text) {
        text = StringUtils.isBlank(text) ? "" : text.trim();
        if (text.length() < 15) {
            text = " " + text;
        }
        while (text.length() < 15) {
            text = text + " ";
        }
        return text.substring(0, 15).replace("%", "%%");
    }

    /**
     * 从数据库获取考生答题信息
     * @param examPaper
     * @return
     */
    private ExamPaperReponseBody getExamineePaperFromDB(ExamPaperReponseBody examPaper,List<ExamineeAnswer> currentExamineeAnswers) {
        for(int i=0;i<examPaper.getGroups().size();i++){
            for(QuestionBase question:examPaper.getGroups().get(i).getQuestions()){
                buildExamineeAnswersFromDB(question.getQuestionType(),question,currentExamineeAnswers);
                //System.out.println(JSONObject.toJSONString(question));
            }
        }
        return examPaper;
    }

    /**
     * 生成word内容
     *
     * @param examPaper
     * @param paperName 导出试卷名称
     */
    private String createWord(ExamPaperReponseBody examPaper,List<Integer> questionTypes,String paperName,Boolean showScoreAndExamineeAnswer, Boolean showAnswer,String fileName,Map<String, Object> allData,String dirPath) {
        String filePath = "";
        Documents.DocumentBuilder of = Documents.of();
        PoitlUtil.createHeader(of, questionTypes);
        allData.put("paperName",paperName);
        List<QuestionTemplate> questions = new ArrayList<>();
        HashMap<Integer,String> groups = new HashMap<>();
        Integer groupIndex = 0;
        for(int i=0;i<examPaper.getGroups().size();i++){
            String groupInfo = examPaper.getGroups().get(i).getGroupName();
            //动态组卷特殊处理，非动态组卷可通过前端维护
            /*if(!ComposeTypeEnum.Artificial.getCode().equals(examPaper.getComposeType())) {
                groupInfo = String.format("%s、%s", NumUtil.convert(i + 1), groupInfo);
            }*/
            groupInfo = String.format("%s、%s", NumUtil.convert(i + 1), groupInfo);
            List groupQuestions = examPaper.getGroups().get(i).getQuestions();
            groups.put(groupIndex,groupInfo);
            groupIndex = groupIndex +groupQuestions.size();
            List<QuestionTemplate> templates = questionBaseService.buildQuestionTemplate(groupQuestions,QuestionSourceEnum.PAPER);
            questions.addAll(templates);
        }
        int[] array = IntStream.range(0, questions.size())
                .filter(i -> questions.get(i).getRepoName() != null).toArray();
        AtomicInteger questionNo = new AtomicInteger();
        for (int i = 0; i < array.length; i++) {
            try {
                for(Map.Entry<Integer,String>group:groups.entrySet()){
                    if(group.getKey()==i){
                        PoitlUtil.createGroupInfo(of,group.getValue());
                        break;
                    }
                }
                int fromIndex = array[i];
                int toIndex = i<array.length-1?array[i+1]:questions.size();
                List<QuestionTemplate> templates = questions.subList(fromIndex,toIndex);
                //System.out.println(templates);
                Map<String, Object> datas = PoitlUtil.createQuestion(of,templates, questionNo, ImportQuestionTypeEnum.试卷,showScoreAndExamineeAnswer,showAnswer);
                allData.putAll(datas);
            } catch (Exception e) {
                log.error("导出word:", e);
                throw new ServiceException("导出word:" + e.getMessage());
            }
        }

        DocumentRenderData data = of.create();
        XWPFTemplate template = XWPFTemplate.create(data);
        try {
            String docPath = dirPath + File.separator + String.format("%s.doc", UUID.randomUUID());
            template.writeToFile(docPath);
            //渲染图片处理
            XWPFTemplate templateRender = XWPFTemplate.compile(docPath);
            handleA3(templateRender);
            templateRender.render(allData);
            filePath = dirPath + File.separator + String.format("%s.doc", fileName);
            templateRender.writeToFile(filePath);
            templateRender.close();
            //todo 清空临时word临时文件
        } catch (Exception e) {
            e.printStackTrace();
            log.error("导出word", e);
        }
        return filePath;
    }

    /**
     * 生成A3试卷
     * @param templateRender
     */
    private void handleA3(XWPFTemplate templateRender){
        CTBody body = templateRender.getXWPFDocument().getDocument().getBody();
        if (!body.isSetSectPr()) {
            body.addNewSectPr();
        }
        CTSectPr section = body.getSectPr();
        if (!section.isSetPgSz()) {
            section.addNewPgSz();
        }
        if (!section.isSetCols()) {
            section.addNewCols();
        }
        CTColumns cols = section.getCols();
        cols.setSpace(BigInteger.valueOf(1000));
        cols.setNum(BigInteger.valueOf(2));
        CTPageSz pageSize = section.getPgSz();
        pageSize.setW(BigInteger.valueOf(23800));
        pageSize.setH(BigInteger.valueOf(16840));
        //横向 页面排版
        pageSize.setOrient(STPageOrientation.PORTRAIT);
    }

    /**
     * 获取试卷的题型
     * @param id
     * @return
     */
    private List<Integer> getQuestionTypes(Integer id){
        List<Integer> questionTypes = examPaperExamineeMapper.selectExamineeQuestionTypes(id);
        return questionTypes;
    }

    @Override
    public MonitorResult exportMonitor(String uuid) {
        MonitorResult result = new MonitorResult();
        int failAmount = 0;
        int successAmount = 0;
        int presetAmount = 0;
        if (redisCache.hasKey(String.format(ExamConstants.EXAM_EXPORT_PRESET_AMOUNT,uuid))) {
            presetAmount = redisCache.getCacheObject(String.format(ExamConstants.EXAM_EXPORT_PRESET_AMOUNT,uuid));
            if (presetAmount <0) {
                result.setStatus("0");
                result.setResult("试卷加载中");
            }
        } else {
            throw new ServiceException("导出处理流水号不存在！");
        }

        List<String> failInfoList = redisCache.getCacheList(String.format(ExamConstants.EXAM_EXPORT_FAIL_INFO,uuid));
        if (redisCache.hasKey(String.format(ExamConstants.EXAM_EXPORT_SUCCESS_AMOUNT,uuid))) {
            successAmount = redisCache.getCacheObject(String.format(ExamConstants.EXAM_EXPORT_SUCCESS_AMOUNT,uuid));;
        }

        StringBuilder importInfo = new StringBuilder();
        if (failInfoList != null) {
            failAmount = failInfoList.size();
            int i = failAmount;
            for (String failInfo : failInfoList) {
                importInfo.insert(0, String.format("<br/>%s、%s",i--, failInfo));
            }
        }
        int currentAmount = failAmount + successAmount;
        if (failAmount > 0) {
            importInfo.insert(0, String.format("导出失败%s条数据，错误如下：",failAmount));
        }
        if (currentAmount == presetAmount) {
            result.setStatus("1");
            if(successAmount==presetAmount) {
                importInfo.insert(0, String.format("恭喜您，数据已全部导出成功！共%s条。", presetAmount));
            }else {
                importInfo.insert(0,  String.format("导出结束，共%s条，导出成功%s条。<br/>",presetAmount,successAmount));
            }
        } else {
            result.setStatus("0");
            importInfo.insert(0,  String.format("导出中，共%s条，已处理%s条,导出成功%s条。<br/>",presetAmount,currentAmount,successAmount));
        }
        result.setResult(importInfo.toString());
        return result;
    }

    /**
     *
     * @param response
     * @param uuid
     */
    @Override
    public void downLoadExport(HttpServletResponse response, String uuid) {
        String dirPath = EightConfig.getProfile()+ File.separator +"zip";
        String zipFileName = "exam_" + uuid + ".zip";
        String zipFilePath = dirPath + File.separator + zipFileName;
        //直接下载
            File zipFile = new File(zipFilePath);
            if (!zipFile.exists()) {
                throw new ServiceException("导出文件不存在");
            }
            try {
                FileUtil.downloadFile(zipFilePath, zipFileName, response);
                zipFile.delete();
            } catch (Exception e) {
                e.printStackTrace();
                throw new ServiceException("导出文件异常");
            }
    }

    /**
     * 从数据库获取考试答卷信息
     * @param id
     * @param userId
     * @return
     */
    @Override
    public ExamPaperReponseBody selectExamPaperByIdAndUserId(Integer id, Long userId) {
        ExamPaper paper = examPaperMapper.selectExamPaperById(id);
        List<ExamineeAnswer> examineeAnswers = examPaperAnswerMapper.selectExamineeAnswersByUserId(id,userId);
        if(ComposeTypeEnum.Dynamics.getCode().equals(paper.getComposeType())){
            ExamPaperExaminee examPaperExaminee = new ExamPaperExaminee();
            examPaperExaminee.setPaperId(id);
            examPaperExaminee.setUserId(userId.intValue());
            List<ExamPaperExamineeResponse> list = examPaperExamineeService.selectExamPaperExamineeList(examPaperExaminee);
            ExamPaperReponseBody examPaper;
            if(list.size()==1){
                examPaper = selectDynamicsExamPaperFromDB(paper,list.get(0).getQuestionOrder(),"0");
                getExamineePaperFromDB(examPaper,examineeAnswers);
            }else {
                examPaper = selectDynamicsExamPaper(paper,"1");
            }
            return examPaper;
        }else {
            ExamPaperReponseBody examPaper = selectConfigExamPaper(paper,"1");
            getExamineePaperFromDB(examPaper,examineeAnswers);
            return examPaper;
        }
    }

    /**
     * 复制试卷
     * @param id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void copyPaper(Integer id) {
        ExamPaper paper = examPaperMapper.selectExamPaperById(id);
        //默认未发布
        paper.setStatus("0");
        ComposeTypeEnum composeTypeEnum = ComposeTypeEnum.fromCode(paper.getComposeType());
        if(composeTypeEnum.equals(ComposeTypeEnum.Dynamics)){
            ExamPaperConfig config = new ExamPaperConfig();
            config.setPaperId(paper.getId());
            List<ExamPaperConfig> configs = examPaperConfigMapper.selectExamPaperConfigList(config);
            ExamPaperBody examPaperBody = new ExamPaperBody();
            BeanUtils.copyProperties(paper,examPaperBody);
            examPaperBody.setId(null);
            configs.stream().forEach(cg->{
                if(StringUtils.isNotBlank(cg.getLevels())){
                    cg.setLevelList(cg.getLevels().split(", "));
                }
                if(StringUtils.isNotBlank(cg.getSections())){
                    cg.setSectionList(cg.getSections().split(", "));
                }
            });
            examPaperBody.setConfigs(configs);
            createExamPaper(examPaperBody);
        }else {
            ExamPaperBody examPaperBody = new ExamPaperBody();
            BeanUtils.copyProperties(paper,examPaperBody);
            examPaperBody.setId(null);

            ExamPaperGroup query = new ExamPaperGroup();
            query.setPaperId(paper.getId());
            List<ExamPaperGroupBoby> groups = examPaperGroupMapper.selectExamPaperGroupBodyList(query);
            examPaperBody.setGroups(groups);
            createExamPaper(examPaperBody);
        }
    }

    /**
     * 加载考生到redis阅卷列表
     * @param paperId
     */
    @Override
    public void loadExamineeMarkQueue(Integer paperId) {
        List<ExamineeMarkQueue> examinees = examPaperExamineeMapper.selectExamPaperExamineeMarkQueue(paperId);
        if(examinees.size()>0){
            String queueKey = String.format("%s:%s", ExamConstants.EXAMINEE_MARK_QUEUE,paperId);
            for(ExamineeMarkQueue examineeMarkQueue:examinees) {
                redisCache.setCacheLeftPush(queueKey, examineeMarkQueue);
            }
        }
    }

    @Override
    public boolean face(MultipartFile file, Integer paperId,String imageType) {
        LoginUser loginUser = getLoginUser();
        if(StringUtils.isNotBlank(loginUser.getUser().getPhoto())) {
            if("1".equals(imageType)) {
                ExamPaper paper = examPaperMapper.selectExamPaperById(paperId);
                if (!paper.getPatterns().contains("1")) {
                    imageType = "3";
                }
            }
            boolean result = true;
            if(!"3".equals(imageType)) {
                String source = loginUser.getUser().getPhoto().replace("/profile", EightConfig.getProfile());
                try {
                    float score = FaceEngineUtil.compare(file.getBytes(), source, libPath);
                    result = score > faceSimilar;
                } catch (Exception e) {
                    result = false;
                }
            }
            Integer userId = getUserId().intValue();
            boolean finalResult = result;
            String finalImageType = imageType;
            threadPoolTaskExecutor.execute(()->{
                savePaperImage(file,paperId,userId, finalImageType, finalResult);
            });
            return result;
        }else {
            throw new ServiceException("人脸识别失败，未上传考生头像");
        }
    }
   private void savePaperImage(MultipartFile file, Integer paperId,Integer userId,String imageType,boolean result){
       String filePath = EightConfig.getUploadPath();
       try {
           String fileName = FileUploadUtils.upload(filePath, file);
           ExamPaperImage examPaperImage = new ExamPaperImage();
           examPaperImage.setPaperId(paperId);
           examPaperImage.setUserId(userId);
           examPaperImage.setImageType(imageType);
           examPaperImage.setImage(fileName);
           examPaperImage.setResult(result?"Y":"N");
           examPaperImage.setCreateTime(new Date());
           examPaperImageService.insertExamPaperImage(examPaperImage);
       } catch (IOException e) {
           throw new RuntimeException(e);
       }

   }

    /**
     * 从数据库读取考生动态组卷答题信息
     * @param questionOrder
     * @param type
     * @return
     */
    private ExamPaperReponseBody selectDynamicsExamPaperFromDB(ExamPaper paper,String questionOrder, String type) {
        ExamPaperConfig config = new ExamPaperConfig();
        config.setPaperId(paper.getId());
        ExamPaperBody examPaperBody = new ExamPaperBody();
        BeanUtils.copyProperties(paper,examPaperBody);
        List<ExamPaperConfig> configs = examPaperConfigMapper.selectExamPaperConfigList(config);
        examPaperBody.setConfigs(configs);
        ExamPaperReponseBody body = getQuestionsByQuestionOrder(examPaperBody,questionOrder,type);
        if(StringUtils.isNotBlank(paper.getGrades())) {
            Integer[] ids = Arrays.stream(paper.getGrades().split(",")).map(Integer::valueOf).toArray(Integer[]::new);
            List<UserInfo> users = examPaperMapper.selectExamUserList(ids);
            body.setGradeUsers(users);
        }
        return body;
    }

    private ExamPaperReponseBody getQuestionsByQuestionOrder(ExamPaperBody examPaperBody, String questionOrder, String type) {
        ExamPaperReponseBody body = new ExamPaperReponseBody();
        List<ExamPaperGroupReponse> groups = new ArrayList<>();
        List<QuestionOrder> questionOrders = JSONArray.parseArray(questionOrder,QuestionOrder.class);
        examPaperBody.getConfigs().stream().forEach(config->{
            String questionType = config.getQuestionType();
            if(StringUtils.isNotNull(config.getQuestionAmount())&&config.getQuestionAmount()>0){
                ExamPaperGroupReponse groupReponseBoby=new ExamPaperGroupReponse();
                groupReponseBoby.setGroupName(QuestionTypeEnum.fromCode(questionType).getName());
                groupReponseBoby.setDefaultScore(config.getDefaultScore());
                groupReponseBoby.setConfigId(config.getId());
                List<QuestionBase> questions = new ArrayList<>();
                for(int i=0;i<questionOrders.size();i++) {
                    if (groupReponseBoby.getGroupName().equals(questionOrders.get(i).getGroupName())) {
                        Integer[] ids =  questionOrders.get(i).getQuestions().stream().map(a->a.getVersionId()).toArray(Integer[]::new);
                        if(ids.length>0) {
                            List<QuestionVersion> vlist = questionVersionMapper.selectQuestionVersionListByIds(ids);
                            vlist.stream().forEach(version -> {
                                if (version != null) {
                                    QuestionBase questionBase = questionBaseService.selectQuestionBodyFromVersion(version.getBaseId(), config.getDefaultScore(), type, version, version.getId());
                                    questions.add(questionBase);
                                }
                            });
                        }
                    }
                }
                groupReponseBoby.setQuestions(questions);
                groups.add(groupReponseBoby);
            }
        });
        BeanUtils.copyProperties(examPaperBody, body);
        body.setGroups(groups);
        return body;
    }

    /**
     * 获取B1、A3A4题型每小题分值
     * @return
     */
    private BigDecimal getSubQuestionScore(String subScores,BigDecimal score, Integer  questionCount,Integer index){
        String[] scores = getScores(subScores);
        BigDecimal subScore;
        if(scores.length==questionCount){
            subScore = new BigDecimal(scores[index]);
        }else {
            subScore = getSubQuestionScoreByDivide(score,questionCount);
        }
        return subScore;
    }

    /**
     * 获取B1、A3A4题型每小题分值，按照平均计算
     * @return
     */
    private BigDecimal getSubQuestionScoreByDivide(BigDecimal score, Integer  questionCount){
        if(!StringUtils.isNull(score)){
            return new BigDecimal(Arith.div(score.doubleValue(), questionCount, 1));
        }else {
            return BigDecimal.ZERO;
        }
    }

    /**
     * 考试提交试题解析成ExamPaperAnswer结构
     * @param examineeId
     * @param questionType
     * @param question
     * @param paperAnswers
     * @param createTime
     */
    private BigDecimal buildExamineeAnswers(Integer examineeId,String questionType,JSONObject question,List<ExamPaperAnswerBody> paperAnswers,Date createTime,HashMap questionMap,ComposeTypeEnum composeTypeEnum,BigDecimal defaultScore){
        BigDecimal totalScore =  BigDecimal.ZERO;
        question.put(ExamConstants.MARK,1);
        if(SingleChoice.getCode().equals(questionType)||MultipleChoice.getCode().equals(questionType)||IMAGE.getCode().equals(questionType)) {
            QuestionChoiceBody submitBody = JSONObject.parseObject(question.toJSONString(), QuestionChoiceBody.class);
            String submitAnswer = "";
            for(QuestionOption option:submitBody.getOptions()){
                if(option.getAnswerFlag()) {
                    if(StringUtils.isBlank(submitAnswer)){
                        submitAnswer = option.getSeqCode();
                    }else {
                        submitAnswer = submitAnswer + "," + option.getSeqCode();
                    }
                }
            }
            QuestionBase questionBase = questionBaseService.selectQuestionBody(submitBody.getId(),null, AnswerShowTypeEnum.True.getCode(),submitBody.getVersionId(),submitBody.getPaperQuestionId());
            QuestionChoiceBody body = JSONObject.parseObject(JSONObject.toJSONString(questionBase), QuestionChoiceBody.class);
            String answer = "";
            for(QuestionOption option:body.getOptions()){
                if(option.getAnswerFlag()) {
                    if(StringUtils.isBlank(answer)){
                        answer = option.getSeqCode();
                    }else {
                        answer = answer + "," + option.getSeqCode();
                    }
                }
            }
            Integer correct = 0;
            if(submitAnswer.equals(answer)){
                correct = 1;
            }
            ExamPaperAnswerBody examPaperAnswer = initExamPaperAnswer(examineeId,submitBody.getPaperQuestionId(),submitBody.getVersionId(),1,submitAnswer,createTime,correct,questionType);
            BigDecimal examineeScore = BigDecimal.ZERO;
            if(correct==1){
                if(ComposeTypeEnum.Dynamics.equals(composeTypeEnum)) {
                    examineeScore = defaultScore;
                }else {
                    ExamPaperQuestionBody questionBody = (ExamPaperQuestionBody)questionMap.get(submitBody.getPaperQuestionId());
                    if (questionBody != null) {
                        examineeScore = questionBody.getScore();
                    }
                }
            }
            examPaperAnswer.setScore(examineeScore);
            paperAnswers.add(examPaperAnswer);
            question.put(ExamConstants.EXAMINEESCORE,examineeScore);
            question.put(ExamConstants.CORRECT,correct);
            question.put(ExamConstants.ANSWER,answer);
            question.put(ExamConstants.EXPLAIN,body.getExplain());
            totalScore = examineeScore;
        }else if(GapFilling.getCode().equals(questionType)||Explain.getCode().equals(questionType)||ShortAnswer.getCode().equals(questionType)||Analysis.getCode().equals(questionType)||Discuss.getCode().equals(questionType)) {
            QuestionAnswerBody submitBody = JSONObject.parseObject(question.toJSONString(), QuestionAnswerBody.class);
            QuestionBase questionBase = questionBaseService.selectQuestionBody(submitBody.getId(),null, AnswerShowTypeEnum.True.getCode(),submitBody.getVersionId(),submitBody.getPaperQuestionId());
            QuestionAnswerBody body = JSONObject.parseObject(JSONObject.toJSONString(questionBase), QuestionAnswerBody.class);
            List<String> submitAnswers= submitBody.getAnswers().stream().map(QuestionAnswer::getExamineeAnswer).collect(Collectors.toList());
            List<String> answers = body.getAnswers().stream().map(QuestionAnswer::getAnswer).collect(Collectors.toList());

            ExamPaperQuestionBody questionBody = (ExamPaperQuestionBody)questionMap.get(submitBody.getPaperQuestionId());
            int correctCount = 0;
            if(submitAnswers.size()==answers.size()) {
                for(int i=0;i<submitAnswers.size();i++){
                    Integer correct = 0;
                    if(submitAnswers.get(i).trim().equals(answers.get(i).trim())){
                        correct = 1;
                        correctCount++;
                    }
                    ExamPaperAnswerBody examPaperAnswer = initExamPaperAnswer(examineeId, submitBody.getPaperQuestionId(), submitBody.getVersionId(), (i+1), submitAnswers.get(i), createTime, correct, questionType);
                    BigDecimal examineeScore = BigDecimal.ZERO;
                    BigDecimal subScore;
                    if(ComposeTypeEnum.Dynamics.equals(composeTypeEnum)) {
                        subScore = getSubQuestionScoreByDivide(defaultScore,answers.size());
                    }else {
                        subScore = getSubQuestionScore(questionBody.getSubScores(),questionBody.getScore(),answers.size(),i);
                    }
                    if(correct == 1){
                        examineeScore = subScore;
                        totalScore = totalScore.add(examineeScore);
                    }

                    setObjectiveQuestionSubScores(question.getJSONArray(ExamConstants.QUESTION_ANSWERS).getJSONObject(i),subScore,examineeScore,answers.get(i).trim());
                    examPaperAnswer.setScore(examineeScore);
                    paperAnswers.add(examPaperAnswer);
                }
                question.put(ExamConstants.CORRECT,correctCount==submitAnswers.size()?1:0);
                //主观题自动阅卷
                question.put(ExamConstants.MARK,1);
            }else {
                AtomicInteger index = new AtomicInteger();
                submitBody.getAnswers().stream().forEach(answer -> {
                    ExamPaperAnswerBody examPaperAnswer = initExamPaperAnswer(examineeId, submitBody.getPaperQuestionId(), submitBody.getVersionId(), Integer.parseInt(answer.getSeqCode()), answer.getAnswer(), createTime, null, questionType);
                    BigDecimal subScore = getSubQuestionScore(questionBody.getSubScores(),questionBody.getScore(),answers.size(),index.getAndIncrement());
                    setObjectiveQuestionSubScores(question.getJSONArray(ExamConstants.QUESTION_ANSWERS).getJSONObject(index.intValue()),subScore,BigDecimal.ZERO,"");
                    examPaperAnswer.setScore(BigDecimal.ZERO);
                    paperAnswers.add(examPaperAnswer);
                });
                question.put(ExamConstants.CORRECT,"");
                question.put(ExamConstants.MARK,0);
            }
            question.put(ExamConstants.EXAMINEESCORE,totalScore);
            question.put(ExamConstants.EXPLAIN,body.getExplain());
        }else if(B1.getCode().equals(questionType)){
            QuestionB1Body submitBody= JSONObject.parseObject(question.toJSONString(), QuestionB1Body.class);
            QuestionBase questionBase = questionBaseService.selectQuestionBody(submitBody.getId(),null, AnswerShowTypeEnum.True.getCode(),submitBody.getVersionId(),submitBody.getPaperQuestionId());
            QuestionB1Body body = JSONObject.parseObject(JSONObject.toJSONString(questionBase), QuestionB1Body.class);
            List<String> submitAnswers= submitBody.getChoices().stream().map(B1Choice::getExamineeAnswer).collect(Collectors.toList());
            List<String> answers= body.getChoices().stream().map(B1Choice::getAnswer).collect(Collectors.toList());
            int correctCount = 0;
            ExamPaperQuestionBody questionBody = (ExamPaperQuestionBody)questionMap.get(submitBody.getPaperQuestionId());
            if(submitAnswers.size()==answers.size()) {
                for(int i=0;i<submitAnswers.size();i++) {
                    Integer correct = 0;
                    if(submitAnswers.get(i).equals(answers.get(i))){
                        correct = 1;
                    }
                    ExamPaperAnswerBody examPaperAnswer = initExamPaperAnswer(examineeId, submitBody.getPaperQuestionId(), submitBody.getVersionId(), (i+1), submitAnswers.get(i), createTime, correct, questionType);
                    BigDecimal subScore;
                    if(ComposeTypeEnum.Dynamics.equals(composeTypeEnum)) {
                        subScore = getSubQuestionScoreByDivide(defaultScore,answers.size());
                    }else {
                        subScore = getSubQuestionScore(questionBody.getSubScores(),questionBody.getScore(),answers.size(),i);
                    }
                    BigDecimal examineeScore = BigDecimal.ZERO;
                    if(correct==1){
                        examineeScore = subScore;
                        examPaperAnswer.setScore(examineeScore);
                        totalScore = totalScore.add(examineeScore);
                        correctCount++;
                    }
                    setB1QuestionSubScores(question.getJSONArray(ExamConstants.QUESTION_CHOICES).getJSONObject(i),body.getChoices().get(i),subScore,examineeScore);
                    examPaperAnswer.setScore(examineeScore);
                    paperAnswers.add(examPaperAnswer);
                }
            }
            question.put(ExamConstants.EXAMINEESCORE,totalScore);
            question.put(ExamConstants.CORRECT,correctCount==submitAnswers.size()?1:0);
        }else if(A3A4Single.getCode().equals(questionType)||A3A4Multiple.getCode().equals(questionType)) {
            QuestionA3A4Body submitBody = JSONObject.parseObject(question.toJSONString(), QuestionA3A4Body.class);
            QuestionBase questionBase = questionBaseService.selectQuestionBody(submitBody.getId(),null, AnswerShowTypeEnum.True.getCode(),submitBody.getVersionId(),submitBody.getPaperQuestionId());
            QuestionA3A4Body body = JSONObject.parseObject(JSONObject.toJSONString(questionBase), QuestionA3A4Body.class);
            List<List<QuestionOption>> submitAnswers= submitBody.getQuestions().stream().map(QuestionChoice::getOptions).collect(Collectors.toList());
            List<List<QuestionOption>>  answers= body.getQuestions().stream().map(QuestionChoice::getOptions).collect(Collectors.toList());
            int correctCount = 0;
            if(submitAnswers.size()==answers.size()) {
                for(int i=0;i<submitAnswers.size();i++) {
                    String submitAnswer = "";
                    for(QuestionOption option:submitAnswers.get(i)){
                        if(option.getAnswerFlag()) {
                            if(StringUtils.isBlank(submitAnswer)){
                                submitAnswer = option.getSeqCode();
                            }else {
                                submitAnswer = submitAnswer + "," + option.getSeqCode();
                            }
                        }
                    }
                    String answer = "";
                    for(QuestionOption option:answers.get(i)){
                        if(option.getAnswerFlag()) {
                            if(StringUtils.isBlank(answer)){
                                answer = option.getSeqCode();
                            }else {
                                answer = answer + "," + option.getSeqCode();
                            }
                        }
                    }

                    Integer correct = 0;
                    if(submitAnswer.equals(answer)){
                        correct = 1;
                    }

                    ExamPaperAnswerBody examPaperAnswer = initExamPaperAnswer(examineeId, submitBody.getPaperQuestionId(), submitBody.getVersionId(), (i+1), submitAnswer, createTime, correct, questionType);
                    ExamPaperQuestionBody questionBody = (ExamPaperQuestionBody)questionMap.get(submitBody.getPaperQuestionId());
                    BigDecimal subScore;
                    if(ComposeTypeEnum.Dynamics.equals(composeTypeEnum)) {
                        subScore = getSubQuestionScoreByDivide(defaultScore,answers.size());
                    }else {
                        subScore = getSubQuestionScore(questionBody.getSubScores(),questionBody.getScore(),answers.size(),i);
                    }
                    BigDecimal examineeScore = BigDecimal.ZERO;
                    if(correct==1){
                        examineeScore = subScore;
                        examPaperAnswer.setScore(examineeScore);
                        totalScore = totalScore.add(examineeScore);
                        correctCount++;
                    }
                    setA3A4QuestionSubScores(question.getJSONArray(ExamConstants.A3A4_QUESTIONS).getJSONObject(i),body.getQuestions().get(i),subScore,examineeScore,answer);
                    examPaperAnswer.setScore(examineeScore);
                    paperAnswers.add(examPaperAnswer);
                }
            }
            question.put(ExamConstants.EXAMINEESCORE,totalScore);
            question.put(ExamConstants.CORRECT,correctCount==submitAnswers.size()?1:0);
        }else if(TrueFalse.getCode().equals(questionType)){
            QuestionTFBody submitBody= JSONObject.parseObject(question.toJSONString(), QuestionTFBody.class);
            QuestionBase questionBase = questionBaseService.selectQuestionBody(submitBody.getId(),null, AnswerShowTypeEnum.True.getCode(),submitBody.getVersionId(),submitBody.getId());
            QuestionTFBody body = JSONObject.parseObject(JSONObject.toJSONString(questionBase), QuestionTFBody.class);
            Integer correct = 0;
            if(submitBody.getAnswerFlag().equals(body.getAnswerFlag())){
                correct = 1;
            }
            ExamPaperAnswerBody examPaperAnswer = initExamPaperAnswer(examineeId, submitBody.getPaperQuestionId(), submitBody.getVersionId(), 1, submitBody.getAnswerFlag(), createTime, correct, questionType);
            BigDecimal examineeScore = BigDecimal.ZERO;
            if(correct==1){
                if(ComposeTypeEnum.Dynamics.equals(composeTypeEnum)) {
                    examineeScore = defaultScore;
                }else {
                    ExamPaperQuestionBody questionBody = (ExamPaperQuestionBody)questionMap.get(submitBody.getPaperQuestionId());
                    if (questionBody != null) {
                        examineeScore = questionBody.getScore();
                    }
                }
            }
            examPaperAnswer.setScore(examineeScore);
            paperAnswers.add(examPaperAnswer);
            question.put(ExamConstants.EXAMINEESCORE,examineeScore);
            question.put(ExamConstants.CORRECT,correct);
            question.put(ExamConstants.ANSWER,"1".equals(body.getAnswerFlag())?"是":"否");
            question.put(ExamConstants.EXPLAIN,body.getExplain());
            totalScore = examineeScore;
        }
        return totalScore;
    }

    /**
     * 从数据库读取考生答题信息
     * @param questionType
     * @param question
     * @param currentExamineeAnswers
     * @return
     */
    private BigDecimal buildExamineeAnswersFromDB(String questionType,QuestionBase question, List<ExamineeAnswer> currentExamineeAnswers){
        BigDecimal totalScore =  BigDecimal.ZERO;
        if(SingleChoice.getCode().equals(questionType)||MultipleChoice.getCode().equals(questionType)||IMAGE.getCode().equals(questionType)) {
            QuestionChoiceBody body = (QuestionChoiceBody)question;
            ExamineeAnswer currentAnswer = currentExamineeAnswers.stream().filter(a->a.getVersionId().equals(body.getVersionId())).findFirst().orElse(null);
            String submitAnswer = "";
            String answer = "";
            BigDecimal examieeScore = BigDecimal.ZERO;
            if(currentAnswer!=null){
                submitAnswer = currentAnswer.getAnswer();
                examieeScore = currentAnswer.getScore();
                body.setCorrect(currentAnswer.getCorrect());
            }
            for(QuestionOption option:body.getOptions()){
                if(option.getAnswerFlag()) {
                    if(StringUtils.isBlank(answer)){
                        answer = option.getSeqCode();
                    }else {
                        answer = answer + "," + option.getSeqCode();
                    }
                    option.setAnswerFlag(false);
                }
                //重新设置选项为考生答题结果
                if(submitAnswer.contains(option.getSeqCode())){
                    option.setAnswerFlag(true);
                }
            }
            body.setExamineeScore(examieeScore);
            body.setAnswer(answer);
            body.setIsExaminee(true);
        }else if(GapFilling.getCode().equals(questionType)||Explain.getCode().equals(questionType)||ShortAnswer.getCode().equals(questionType)||Analysis.getCode().equals(questionType)||Discuss.getCode().equals(questionType)) {
            QuestionAnswerBody body = (QuestionAnswerBody)question;
            List<ExamineeAnswer> currentAnswers = currentExamineeAnswers.stream().filter(a->a.getVersionId().equals(body.getVersionId())).collect(Collectors.toList());
            Integer correct = 1;
            for(ExamineeAnswer answer:currentAnswers){
                for(QuestionAnswer questionAnswer:body.getAnswers()){
                    if(answer.getSeqNo().equals(questionAnswer.getSeqCode())){
                        questionAnswer.setExamineeAnswer(answer.getAnswer());
                        questionAnswer.setExamineeScore(answer.getScore());
                        totalScore = totalScore.add(answer.getScore());
                        questionAnswer.setCorrect(answer.getCorrect());
                        BigDecimal subScore = getSubQuestionScore(body.getSubScores(),body.getScore(),body.getAnswers().size(),Integer.parseInt(answer.getSeqNo())-1);
                        questionAnswer.setScore(subScore);
                        if(answer.getCorrect()==0){
                            correct = 0;
                        }
                    }
                }
            }
            body.setCorrect(correct);
            body.setIsExaminee(true);
            body.setExamineeScore(totalScore);
        }else if(B1.getCode().equals(questionType)){
            QuestionB1Body body = (QuestionB1Body)question;
            List<ExamineeAnswer> currentAnswers = currentExamineeAnswers.stream().filter(a->a.getVersionId().equals(body.getVersionId())).collect(Collectors.toList());
            Integer correct = 1;
            if(body.getChoices().size()==currentAnswers.size()) {
                for (int i = 0; i < currentAnswers.size(); i++) {
                    ExamineeAnswer answer = currentAnswers.get(i);
                    B1Choice b1Choice = body.getChoices().get(i);
                    b1Choice.setExamineeAnswer(answer.getAnswer());
                    b1Choice.setExamineeScore(answer.getScore());
                    totalScore = totalScore.add(answer.getScore());
                    b1Choice.setCorrect(answer.getCorrect());
                    BigDecimal subScore = getSubQuestionScore(body.getSubScores(),body.getScore(),currentAnswers.size(),i);
                    b1Choice.setScore(subScore);
                    if(answer.getCorrect()==0){
                        correct = 0;
                    }
                }
            }
            body.setCorrect(correct);
            body.setIsExaminee(true);
            body.setExamineeScore(totalScore);
        }else if(A3A4Single.getCode().equals(questionType)||A3A4Multiple.getCode().equals(questionType)) {
            QuestionA3A4Body body = (QuestionA3A4Body)question;
            List<ExamineeAnswer> currentAnswers = currentExamineeAnswers.stream().filter(a->a.getVersionId().equals(body.getVersionId())).collect(Collectors.toList());
            List<List<QuestionOption>>  answers= body.getQuestions().stream().map(QuestionChoice::getOptions).collect(Collectors.toList());
            Integer correct = 1;
            if(currentAnswers.size()==answers.size()) {
                for(int i=0;i<currentAnswers.size();i++) {
                    String submitAnswer = currentAnswers.get(i).getAnswer();
                    String answer = "";
                    for(QuestionOption option:answers.get(i)){
                        if(option.getAnswerFlag()) {
                            if(StringUtils.isBlank(answer)){
                                answer = option.getSeqCode();
                            }else {
                                answer = answer + "," + option.getSeqCode();
                            }
                            option.setAnswerFlag(false);
                        }
                        //重新设置选项为考生答题结果
                        if(submitAnswer.contains(option.getSeqCode())){
                            option.setAnswerFlag(true);
                        }
                    }
                    body.getQuestions().get(i).setAnswer(answer);
                    body.getQuestions().get(i).setExamineeScore(currentAnswers.get(i).getScore());
                    totalScore = totalScore.add(currentAnswers.get(i).getScore());
                    body.getQuestions().get(i).setCorrect(currentAnswers.get(i).getCorrect());
                    BigDecimal subScore = getSubQuestionScore(body.getSubScores(),body.getScore(),answers.size(),i);
                    body.getQuestions().get(i).setScore(subScore);
                    if(currentAnswers.get(i).getCorrect()==0){
                        correct = 0;
                    }
                }
            }
            body.setCorrect(correct);
            body.setIsExaminee(true);
            body.setExamineeScore(totalScore);
        }else if(TrueFalse.getCode().equals(questionType)){
            QuestionTFBody body = (QuestionTFBody) question;
            ExamineeAnswer currentAnswer = currentExamineeAnswers.stream().filter(a->a.getVersionId().equals(body.getVersionId())).findFirst().orElse(null);
            Integer correct = 0;
            body.setAnswerFlag("");
            if(currentAnswer!=null){
                correct = currentAnswer.getCorrect();
                body.setAnswerFlag(currentAnswer.getAnswer());
                body.setExamineeScore(currentAnswer.getScore());
                totalScore = currentAnswer.getScore();
            }
            body.setAnswer("1".equals(body.getAnswerFlag())?ExamConstants.QUESTION_TRUE:ExamConstants.QUESTION_FALSE);
            body.setCorrect(correct);
            body.setIsExaminee(true);
            body.setExamineeScore(totalScore);
        }
        return totalScore;
    }

    /**
     * 获取子题分值
     * @param subScores
     * @return
     */
    private String[] getScores(String subScores) {
        if(StringUtils.isBlank(subScores)){
            subScores = "";
        }
        return subScores.replace("[", "").replace("]", "").split(",");
    }

    /**
     * 设置B1子题信息
     * @param jsonChoice
     * @param choice
     * @param subScore
     * @param examineeScore
     */
    private void setB1QuestionSubScores(JSONObject jsonChoice, B1Choice choice, BigDecimal subScore, BigDecimal examineeScore) {
        if(StringUtils.isNotNull(jsonChoice)){
            jsonChoice.put(ExamConstants.QUESTION_SCORE,subScore);
            jsonChoice.put(ExamConstants.EXAMINEESCORE,examineeScore);
            jsonChoice.put(ExamConstants.CORRECT,subScore.compareTo(examineeScore)==0?1:0);
            jsonChoice.put(ExamConstants.ANSWER,choice.getAnswer());
            jsonChoice.put(ExamConstants.EXPLAIN,choice.getExplain());
        }
    }

    /**
     * 设置A3A4子题信息
     * @param jsonQuestionChoice
     * @param questionChoice
     * @param subScore
     * @param examineeScore
     */
    private void setA3A4QuestionSubScores(JSONObject jsonQuestionChoice, QuestionChoice questionChoice, BigDecimal subScore, BigDecimal examineeScore,String correctAnswer) {
        if(StringUtils.isNotNull(jsonQuestionChoice)){
            jsonQuestionChoice.put(ExamConstants.QUESTION_SCORE,subScore);
            jsonQuestionChoice.put(ExamConstants.EXAMINEESCORE,examineeScore);
            jsonQuestionChoice.put(ExamConstants.CORRECT,subScore.compareTo(examineeScore)==0?1:0);
            jsonQuestionChoice.put(ExamConstants.ANSWER,correctAnswer);
            jsonQuestionChoice.put(ExamConstants.EXPLAIN,questionChoice.getExplain());
        }
    }

    /**
     * 设置客观题子题分值
     * @param answer
     * @param subScore
     * @param examineeScore
     */
    private void setObjectiveQuestionSubScores(JSONObject answer, BigDecimal subScore, BigDecimal examineeScore,String correctAnswer) {
        if(StringUtils.isNotNull(answer)){
            answer.put(ExamConstants.QUESTION_SCORE,subScore);
            answer.put(ExamConstants.EXAMINEESCORE,examineeScore);
            answer.put(ExamConstants.ANSWER,correctAnswer);
        }
    }

    private ExamPaperAnswerBody initExamPaperAnswer(Integer examineeId,Integer paperQuestionId,Integer versionId,Integer seqNo,String answer,Date createTime,Integer correct,String questionType){
        ExamPaperAnswerBody examPaperAnswer = new ExamPaperAnswerBody();
        examPaperAnswer.setExamineeId(examineeId);
        examPaperAnswer.setPaperQuestionId(paperQuestionId);
        examPaperAnswer.setVersionId(versionId);
        examPaperAnswer.setSeqNo(seqNo);
        examPaperAnswer.setAnswer(answer);
        examPaperAnswer.setCreateTime(createTime);
        examPaperAnswer.setCorrect(correct);
        examPaperAnswer.setQuestionType(questionType);
        examPaperAnswer.setMark(QuestionUtil.checkQuestionType(questionType)?1:0);
        return examPaperAnswer;
    }

    /**
     * 查询考试试卷列表
     * 
     * @param examPaper 考试试卷
     * @return 考试试卷
     */
    @Override
    public List<ExamPaper> selectExamPaperList(ExamPaper examPaper)
    {
        return examPaperMapper.selectExamPaperList(examPaper);
    }

    /**
     * 修改考试试卷
     * 
     * @param examPaper 考试试卷
     * @return 结果
     */
    @Override
    public int updateExamPaper(ExamPaper examPaper)
    {
        examPaper.setUpdateTime(DateUtils.getNowDate());
        return examPaperMapper.updateExamPaper(examPaper);
    }

    /**
     * 批量删除考试试卷
     * 
     * @param ids 需要删除的考试试卷主键
     * @return 结果
     */
    @Override
    public int deleteExamPaperByIds(Integer[] ids)
    {
        return examPaperMapper.deleteExamPaperByIds(ids);
    }

    /**
     * 删除考试试卷信息
     * 
     * @param id 考试试卷主键
     * @return 结果
     */
    @Override
    public int deleteExamPaperById(Integer id)
    {
        return examPaperMapper.deleteExamPaperById(id);
    }

    /**
     * 创建考试试卷
     *
     * @param examPaperBody 考试试卷
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor =  Exception.class)
    public void createExamPaper(ExamPaperBody examPaperBody) {
        examPaperBody.setCreateTime(DateUtils.getNowDate());
        examPaperMapper.insertExamPaper(examPaperBody);
        savePaperDepts(examPaperBody.getId(),examPaperBody.getDepts(),0);
        editPaper(examPaperBody);
    }

    /**
     * 保存试卷考试机构
     * @param paperId 试卷id
     * @param strDepts
     * @param type 0 新增 1 修改
     */
    private void savePaperDepts(Integer paperId, String strDepts, int type) {
        if(type==1){
            examPaperMapper.deleteExamPaperDepts(paperId);
        }
        List<ExamPaperDept> depts = new ArrayList<>();
        Arrays.stream(strDepts.split(",")).forEach(deptId->{
            ExamPaperDept det = new ExamPaperDept();
            det.setPaperId(paperId);
            det.setDeptId(Integer.parseInt(deptId));
            depts.add(det);
        });
        if(depts.size()>0) {
            examPaperMapper.insertExamPaperDepts(depts);
        }
    }

    /**
     * 修改考试试卷
     *
     * @param examPaperBody 考试试卷
     * @return 结果
     */
    @Override
    public void updatePaper(ExamPaperBody examPaperBody) {
        examPaperBody.setUpdateTime(DateUtils.getNowDate());
        examPaperMapper.updateExamPaper(examPaperBody);
        examPaperConfigMapper.deleteExamPaperConfigByPaperId(examPaperBody.getId());
        examPaperGroupMapper.deleteExamPaperGroupByPaperId(examPaperBody.getId());
        examPaperQuestionMapper.deleteExamPaperQuestionByPaperId(examPaperBody.getId());
        savePaperDepts(examPaperBody.getId(),examPaperBody.getDepts(),1);
        editPaper(examPaperBody);
    }

    /**
     * 编辑试卷配置、分组、题目信息
     *
     * @param examPaperBody
     */
    private void editPaper(ExamPaperBody examPaperBody){
        if(ComposeTypeEnum.Dynamics.getCode().equals(examPaperBody.getComposeType())){
            for(ExamPaperConfig examPaperConfig:examPaperBody.getConfigs()) {
                examPaperConfig.setPaperId(examPaperBody.getId());
                if(StringUtils.isNotNull(examPaperConfig.getSectionList())) {
                    String sections = Arrays.toString(examPaperConfig.getSectionList());
                    sections = sections.length() > 0 ? sections.replace("[", "").replace("]", "") : "";
                    examPaperConfig.setSections(sections);
                }
                if(StringUtils.isNotNull(examPaperConfig.getLevelList())) {
                    String levels = Arrays.toString(examPaperConfig.getLevelList());
                    levels = levels.length() > 0 ? levels.replace("[", "").replace("]", "") : "";
                    examPaperConfig.setLevels(levels);
                }
                examPaperConfigMapper.insertExamPaperConfig(examPaperConfig);
                //校验是否可正常分配
                getQuestions(examPaperBody, AnswerShowTypeEnum.False.getCode());
            }
        }else {
            for(ExamPaperGroupBoby examPaperGroupBoby:examPaperBody.getGroups()) {
                examPaperGroupBoby.setPaperId(examPaperBody.getId());
                examPaperGroupMapper.insertExamPaperGroup(examPaperGroupBoby);
                examPaperGroupBoby.getQuestions().stream().forEach(q->{
                    q.setPaperId(examPaperBody.getId());
                    q.setGroupId(examPaperGroupBoby.getId());
                    if(q.getQuestionCount()==null||q.getQuestionCount()==0){
                        q.setQuestionCount(1);
                    }
                });
                examPaperQuestionMapper.insertExamPaperQuestions(examPaperGroupBoby.getQuestions());
            }
        }
    }
}
